package com.bluexmicro.bluetooth.peripheral.handler

import android.annotation.SuppressLint
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothStatusCodes
import android.os.Build
import com.bluexmicro.bluetooth.LogUtils
import com.bluexmicro.bluetooth.bluetoothStatusCodesName
import com.bluexmicro.bluetooth.hasProperty
import com.bluexmicro.bluetooth.model.GattStatus
import com.bluexmicro.bluetooth.model.WriteType
import com.bluexmicro.bluetooth.toHexString
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume


class WriteHandler {

    private var callback: WriteCallback? = null
    private var cancellableContinuation: CancellableContinuation<Result<Any>>? = null

    fun onCharacteristicWrite(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        status: Int
    ) {
        callback?.invoke(gatt, characteristic, status)
    }

    private fun setCallback(cb: WriteCallback) {
        this.callback = cb
    }

    fun isActive(): Boolean {
        return cancellableContinuation?.isActive == true
    }

    @SuppressLint("MissingPermission")
    suspend fun execute(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        type: WriteType,
        value: ByteArray
    ) = suspendCancellableCoroutine<Result<Any>> {
        cancellableContinuation = it
        setCallback { _, c, status ->
            if (c.uuid.equals(characteristic.uuid)) {
                if (it.isActive) {
                    this.callback = null
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        it.resume(Result.success(Unit))
                    } else {
                        val reason = GattStatus.fromValue(status).name
                        it.resume(Result.failure(IllegalStateException("Write response failed, gatt status: $status, reason: $reason")))
                    }
                }
            }
        }
        val writeProperty: Int = when (type) {
            WriteType.WithResponse -> BluetoothGattCharacteristic.PROPERTY_WRITE
            WriteType.WithoutResponse -> BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
            WriteType.Signed -> BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE
        }

        val res: Boolean
        val detail: String
        if (!characteristic.hasProperty(writeProperty)) {
            res = false
            detail = "This characteristic not support ${type.name}"
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                val code = gatt.writeCharacteristic(characteristic, value, type.value)
                res = code == BluetoothStatusCodes.SUCCESS
                detail = code.bluetoothStatusCodesName()
            } else {
                characteristic.writeType = type.value
                characteristic.value = value
                res = gatt.writeCharacteristic(characteristic)
                detail = if (res) "" else "Write was rejected by ststem"
            }
        }
        if (!res) {
            this.callback = null
            it.resume(Result.failure(IllegalArgumentException(detail)))
        }

        LogUtils.sendLog(
            mapOf(
                "mac" to gatt.device.address,
                "action" to "callWrite",
                "service" to characteristic.service.uuid.toString(),
                "characteristic" to characteristic.uuid.toString(),
                "WriteType" to type.name,
                "value" to value.toHexString(),
                "detail" to detail
            )
        )

        it.invokeOnCancellation {
            this.callback = null
        }
    }
}